Fix missing ViewStyle properties on legacy TypeScript ImageStyle - #57938
Fix missing ViewStyle properties on legacy TypeScript ImageStyle#57938giaBaoJS wants to merge 1 commit into
Conversation
The hand-written `ImageStyle` interface in `types_DEPRECATED` redeclared a small subset of `ViewStyle` properties instead of extending it, so newer style props such as `filter`, `boxShadow`, `mixBlendMode`, `outlineColor` and `pointerEvents` were rejected on `<Image>` styles. Both other sources of truth already include them: the Flow `____ImageStyle_InternalCore` spreads `$Exact<____ViewStyle_Internal>`, and the generated strict API declares `Omit<____ViewStyle_Internal, 'overflow'>`. Extend `ViewStyle` with `overflow` omitted, mirroring the strict API, so `overflow` stays narrowed to 'visible' | 'hidden' on images.
|
Hey @giaBaoJS, thanks for looking into this. I don't know if we want to merge this right now, as #52957 was opened in mid-2025 and we've since deprecated the legacy manual types — it's probably better to keep those code-locked rather than delivering a minor fix/breaking change this late in the game. Sorry about that. |
|
Understood — code-locking the legacy manual types is a reasonable call, and better to hear it now than after they diverge further. No objection to closing. One thing worth leaving behind for whoever finds #52957 next: the report names both For |
Summary:
Fixes #52957
The hand-written (legacy) TypeScript
ImageStyleinterface redeclares a small subset ofViewStyle's properties instead of extending it. As a result, style properties added toViewStyleover time —filter,boxShadow,mixBlendMode,outlineColor/outlineOffset/outlineStyle/outlineWidth,pointerEvents,elevation,isolation,borderCurve,backgroundImage, the logical border-radius/border-color properties, and theexperimental_background*set — are rejected on<Image>styles, even though they are supported at runtime.This is a divergence between three sources of truth, not a design choice
ViewStyleprops?Libraries/StyleSheet/StyleSheetTypes.js:1052____ImageStyle_InternalCore = Readonly<{...$Exact<____ViewStyle_Internal>, resizeMode?, objectFit?, tintColor?, overlayColor?, overflow?}>____ViewStyle_InternalReactNativeApi.d.ts:619____ImageStyle_InternalCore = Readonly<Omit<____ViewStyle_Internal, "overflow"> & {...}>types_DEPRECATED/Libraries/StyleSheet/StyleSheetTypes.d.ts:641interface ImageStyle extends FlexStyle, ShadowStyleIOS, TransformsStyleThis matches @huntie's diagnosis in #52957: the bug is in the manual types only, and the
react-native-strict-apiopt-in is already correct.Scope:
ImageStyleonly — theTextStylehalf of the report is already fixed onmainThe issue title says "
ImageStyleandTextStyle", but onlyImageStyleis affected today.StyleSheetTypes.d.ts:562already readsexport interface TextStyle extends TextStyleIOS, TextStyleAndroid, ViewStyle, soTextStyleinherits everything. I verified this with atscprobe against the legacy types: aTextStyleobject literal containingfilter,boxShadow,mixBlendMode,outlineColor, andisolationtypechecks cleanly on unmodifiedmain. Keeping this PR scoped toImageStyleis deliberate.How
overflowis handledImageStylecannot simplyextend ViewStyle:ViewStyleinheritsoverflow?: 'visible' | 'hidden' | 'scroll'fromFlexStyle, while both Flow and the strict API deliberately narrow it to'visible' | 'hidden'for images. This PR mirrors the strict API exactly —extends Omit<ViewStyle, 'overflow'>, then redeclares the narrowedoverflow. There is a type test asserting'scroll'is still rejected.The nine other properties removed from the interface body are now inherited from
ViewStyle. I confirmed each one keeps a byte-identical type viaExact<>type-level assertions (see Test Plan), so this is a pure widening with no change to any previously-valid style.Changelog:
[GENERAL] [FIXED] - Add missing
ViewStyleproperties (filter,boxShadow,mixBlendMode,outlineColor,pointerEvents, and others) to the legacy TypeScriptImageStyletypeTest Plan:
Added
packages/react-native/__typetests__/stylesheet-image-style.tsx, which runs under bothtsconfig.legacy.json(hand-written types) andtsconfig.json(generated strict API), so the two stay aligned.Counterfactual — the test fails without the fix. With only the interface change reverted (test file kept):
Restoring the fix:
User-visible path verified. The second error above is the real-world symptom —
<Image style={{filter: 'brightness(0.5)', mixBlendMode: 'multiply'}} />, i.e. an app author writingViewStyleproperties inline on anImage. (filterproduces a confusing message becauseStyleProp<ImageStyle>may be an array, so TS matches it againstArray.prototype.filter.) The type test covers both this JSX path and directImageStyleannotations.Each property from the issue report, individually, before the fix (
const s: ImageStyle = {<prop>}compiled withtsc -p tsconfig.legacy.json):All pass after the fix.
No regression for existing consumers. Type-level
Exact<A, B>assertions confirm every property removed from the interface body resolves to an identical type through inheritance, and thatoverflow/resizeModeare unchanged — all 12 pass:backfaceVisibility,borderBottomLeftRadius,borderBottomRightRadius,backgroundColor,borderColor,borderRadius,borderTopLeftRadius,borderTopRightRadius,opacity,cursor,overflow,resizeMode.Full gates, all green:
yarn test-typescript-legacyDone in 0.78s)yarn test-generated-typescriptDone in 0.84s)yarn testyarn eslint --max-warnings 0 <changed files>prettier --check <changed files>yarn build-typesgit statusclean, confirming the strict API is untouched